My next project was an attempt at the game Snake. This project was really to help me become even more familiar with object oriented programming. This was my most challenging but rewarding project to date. My basic plan for the project was to have a Game, Snake, Board and Cell class along with the Form class in VB. The board would be made up of cells and the cell could either show an empty space, part of the snake or food. The snake class would store information about the snake and the game class would control the game. I would use to form class to deal with user input events e.g. keypresses.
Key Aspects
The Cells and Board
The first task was to create a display for the user to play on. Within the cell class I used an enumeration to create three different cell types: Empty, Snake or Food. Each cell would have a type property which would store one of these values. For each different cell type, the cell would appear a different colour and I used picture boxes to show this. Upon reflection I feel that using the VB paint event would have been a better approach since it would have made the form more organised. To bring the cells together, I created a board class. The board class stored a two dimensional array of cells which created a grid. This grid was then displayed on the form. The board class is responsible for updating the status of board when different things happen in the game.
The Snake Head
Obviously the next job was to create the snake itself. I started simply by creating a new Point called ‘snakeHead.’ As the name suggests, this is where the location of the head of the snake is stored. This is important since the user will control the snake head. In the Form class I created some event handlers which respond to user inputs (keypresses) by starting timers. These timers call public subroutines within the snake object that will move the snake head. The board class then updates its cells and their types to ensure the snake head has moved. If the X or Y value of the snake head goes off the screen, it will be updated to show the snake head on the opposite side of the screen – this way it cannot disappear.
The Food
Before we can program the body of the snake, we need there to be a reason for the snake to grow. This is where the food comes in and the process is very simple. When the form is loaded, a subroutine in the board class is called to set a random square as food (excluding cells occupied by the snake). The type of the randomly chosen cell is then changed to food and displayed in red on the screen.
Every time the snake eats the food in the future, the same subroutine is called to add another bit of food.
The Body
Now that we have the food, we need a way to convert the food into the snake body when the snake eats it. We first need a subroutine that checks that the coordinate point of the snake head is the same as the coordinate of the food. This will be called every time the snake moves a space. If the snake head does eat the food, an extra snake cell should appear at the end of the snake. To do this I used a ‘linked list’ of cells which stores a list of the cells in the snake’s body. I used a linked list over an array because arrays have lots of issues when it comes to looping through a constantly changing array. A linked list is like a queue and is much more flexible with changing size. A subroutine would then detect where the last snake cell is in the linked list and use its coordinates to convert that last visited cell back into a snake cell when it would have normally been converted into an empty space. This gives the effect that the snake has grown.
Moving the body is also complicated. The snake head needs to move to the space the user decides, meanwhile the body must follow it. To do this, a For loop goes through each cell in the snake body and sets its X and Y coordinates to those of the previous cell. To avoid them all having the same values, the loop starts at the back of the snake body and works forward until it reaches the snake head where it stops.
I finally added a score and a play again button to make the game enjoyable to play. I also added a subroutine which checks if the snake as bitten itself – this is the point that the game ends and the score is displayed.
Challenges in Development
Changing the cell types
Whilst this sounds straight forward I actually had a lot of difficulty with it. For a while, without realising it, I was actually just creating new cells which overlapped the cells beneath it. There were two approaches I could take to fix this and I was not sure which one would work better. The first is make a subroutine which simply changes the ‘type’ property inside the cell. The colour of the cell then adjusts automatically since it is dependant on the cell type. The alternative way of doing it was to set the actual cell and as instance of a new cell with a new type property. This would not actually create another object, it would just change an existing one into a new one. I actually used both methods in the end as both worked better in different situations. When making the separate snake body linked list, it was easier to create new cells to store the position and type of each cell. However, when actually changing values on the board, it was easier to simply change the cell type.
Moving the snake
Moving the snake on the board also proved to be difficult. With the linked list constantly changing values, I would often get overflow errors with the loops. The snake also had to be treated differently when it was only a snake head since it has no body to move. Adding parts to the snake was also challenging since it was necessary to store past position data to create the new space – but only when the snake ate some food. Perhaps a linked list is not the most necessary data type to store the body. An actual queue may have been a better decision.
Areas for Future Improvement
Whilst I am happy with the end result, I know there are lots of improvements that can be made to the code and I look forward to improving myself as a developer. Some areas which need work are:
-
I often had too much going on in one subroutine. By splitting each subroutine up more, into separate subroutines, it could have made the flow of the program much easier to follow. This would have been great for error checking.
-
Using a consistent method for changing the cell types.
-
Using more effective and appropriate data types for different aspects of the project.
-
Make better use of the game class or getting rid of it entirely. Since all user input events, paint events and timer controls must be programmed in the Form class, this left very little for the game class to do other than simply setting off some starting subroutines. This was supposed to be the base class for the game however the Form class took this role. This is something I must work on.
-
Try to use less global variables.
Conclusion
I am confident the program could be greatly improved however I am happy with the end result now. I understand the process and the program and I know where its weakness lie which is the most important thing. I enjoyed the challenge of working in between different classes and having my objects interact with each other. This project was excellent for strengthening my knowledge of object oriented programming which will be very important for my computer science coursework later this year.